Vlakna - zobrazeni informaci uzivateli na jeden form z vice vlaken
Otázka od: Ing. Jiri Sokol
19. 10. 2004 9:42
Ahoj!
Potrebuju nakopnout s viz subjekt. Ted to mam tak, ze hlavni aplikace vytvori
"informacni" formular, na kterym je ListView (aspon doufam, ze se nepletu -
proste pridavam zaznamy do tabulky se 3 sloupci).
Podle potreby tyto vlakna volaji jednu proceduru hlavniho formulare, ktera
zapise na ten infromacni fomrular pozadovane informace.
Tusim, ze toto neni spravna cesta. Automaticky me napada, ze bude problem nekde
se synchronizaci, ale posledne jste mi radili, abych se vyvaroval uziti
Synchronize ve vlaknech, ze pry jinak vlakna ani nemusim pouzivat. K priblizeni
co se deje - pouzivam workflow vlaken Petra Fejfara, akorat ne pres semaphor,
ale pres udalosti (nevznika mi fronta pozadavku). Tyto vlakna vytvorim jednou a
protoze si nactou nejake data z databaze, se kterymi pak pracuji, tak tyto
vlakna ziji po celou dobu zivota programu. Akorat vzdy provedou svoji akci a
pak se "uspi".
Jak to resite teda vy, kdyz mate vice vlaken a potebujete neco uzivateli
napsat?
Diky za rady
Jirka
--------------------------------------------------
Ing. Jiri Sokol; jiri.sokol@seznam.cz; 972 231 187
D6Prof+SP3; WinXPProf+SP1; FB 1.5.0
programator amater
Odpovedá: delphin@post.cz
19. 10. 2004 10:08
> Jak to resite teda vy, kdyz mate vice vlaken a potebujete neco uzivateli
napsat?
Napriklad poslat message hlavnimu vlaknu zpravu pomoci PostMessage.
Odpovedá: Milan Tomes
19. 10. 2004 10:02
Ahoj,
AFAIK tak jak to mas je v poradku a z principu je nutno vsechna volani VCL
synchronizovat prave pres Synchronize.
Hlavni thread aplikace by mel byt vyhrazen interakci s uzivatelem kam
zobrazeni ListView rozhodne spada a tak to mas IMHO naprosto spravne.
Je pravda, ze Synchronize zpusobi to, ze se dana metoda provede v kontextu
main threadu, ale to je presne to o co tady bezi. Jedine upozorneni je
takove, aby dana metoda delala jen a jen to pridani do ListView - tedy
zadnou narocnou akci. Synchronize totiz onen volajici thread pozastavi do
okamziku ukonceni volane metody.
Ja jsem toto vzdy resil pres volani nejake metody formulare pres
Synchronize, ktera si precetla hodnoty z property volajiciho threadu a
provedla akci (v Tvem pripade pridani hodnoty do ListView).
S pozdravem
Milan Tomes
> [mailto:delphi-l-owner@clexpert.cz]On Behalf Of Ing. Jiri Sokol
> Sent: Tuesday, October 19, 2004 10:33 AM
>
> Ahoj!
> Potrebuju nakopnout s viz subjekt. Ted to mam tak, ze hlavni
> aplikace vytvori "informacni" formular, na kterym je ListView
> (aspon doufam, ze se nepletu - proste pridavam zaznamy do tabulky
> se 3 sloupci).
> Podle potreby tyto vlakna volaji jednu proceduru hlavniho
> formulare, ktera zapise na ten infromacni fomrular pozadovane informace.
> Tusim, ze toto neni spravna cesta. Automaticky me napada, ze bude
> problem nekde se synchronizaci, ale posledne jste mi radili,
> abych se vyvaroval uziti Synchronize ve vlaknech, ze pry jinak
> vlakna ani nemusim pouzivat. K priblizeni co se deje - pouzivam
Odpovedá: Petr Fejfar
19. 10. 2004 9:55
Ing. Jiri Sokol wrote:
> problem nekde se synchronizaci, ale posledne jste mi radili, abych se
> vyvaroval uziti Synchronize ve vlaknech, ze pry jinak vlakna ani
> nemusim pouzivat.
Jenomze ty namitky se tykaly toho, ze jsi pomoci Synchronize spoustel cely
kod threadu - to ten thread pak skutecne k nicemu nepotrebujes.
Pokud je delka kodu provadeneho v ramci Synchronize rozumna a aplikaci
nevadi,
ze pokud se sejdou zadosti sychnronize, zastavi se v podstate vsechny
thready,
ktere synchronize volali, tak ji klidne volej, akorat se snaz omezit delku
kodu volaneho pres Synchronize na minimum.
Pokud to vadi nebo pokud se chces soustredit v budoucnu na propracovane
mutlithreaded aplikace, vyhodnejsi ale pracnejsi bude navrhnout si vlastni
IPC
(Inter Process Communication).
HTH, pf
Odpovedá: Milan Tomes
19. 10. 2004 10:46
Ano i to je moznost. Parametry se predaji v l(w)Param treba i pomoci nejake
dynamicke struktury, ale zalezi na casove narocnosti volane akce - aby
nebylo vytvareni a plneni struktury pomalejsi nez provedeni metody pomoci
Synchronize. Ale jde o princip.
Pokud bych mel framework, ktery je postaven na vlaknech, tak bych asi
opravdu volil tuto moznost uz je proto, ze je to *systemove*
Ted jsem si uvedomil, ze to vlastne take tak delam a jako parametr predavam
pointer na dynamicky alokovany buffer se stringem. A tak bych se ted rad
zeptal ja:
1. Muze se stat, ze zprava neni zpracovana ???
2. Pokud ano, tak za jakych podminek ???
3. Pokud ano, tak kde mam zajistit korektni dealokaci pameti ???
Diky
S pozdravem
Milan Tomes
> [mailto:delphi-l-owner@clexpert.cz]On Behalf Of delphin@post.cz
> Sent: Tuesday, October 19, 2004 10:49 AM
>
> > Jak to resite teda vy, kdyz mate vice vlaken a potebujete neco uzivateli
> napsat?
>
> Napriklad poslat message hlavnimu vlaknu zpravu pomoci PostMessage.
>
>
>
>
Odpovedá: delphin@post.cz
19. 10. 2004 11:15
> Ted jsem si uvedomil, ze to vlastne take tak delam a jako parametr
predavam
> pointer na dynamicky alokovany buffer se stringem. A tak bych se ted rad
> zeptal ja:
> 1. Muze se stat, ze zprava neni zpracovana ???
> 2. Pokud ano, tak za jakych podminek ???
> 3. Pokud ano, tak kde mam zajistit korektni dealokaci pameti ???
Zpravy jsou zpracovavany dokud existuje prislusny formular a neni ukoncena
aplikace. Po ukonceni aplikace mohou nektere zpravy zustat nevyrizeny.
Odpovedá: Milan Tomes
19. 10. 2004 11:27
Dobre jak tedy provest korektni dealokaci v tomto pripade:
procedure TG3UDPServer.Execute;
var
Sock: TUDPBlockSocket;
Buf: string;
DBConnection: TDBConnection;
Buf_1: string;
Ptr: Pointer;
begin
SetName;
if Assigned(FDBConnection) then
DBConnection := TDBConnection(FDBConnection)
else
DBConnection := nil;
Sock := TUDPBlockSocket.Create;
try
Sock.Bind(cAnyHost, IntToStr(FPort));
if Sock.LastError <> 0 then
exit;
while True do
begin
if Terminated then
break;
Buf := sock.RecvTerminated(1000, cmd_Terminator) + cmd_Terminator;
if Sock.LastError = 0 then
begin
if Buf = cmd_GetIP then
begin
Sock.SendString(cmd_GetIP_Response);
end;
if Buf = cmd_AppParamsChanged then
PostMessage(Application.MainForm.Handle, WM_PARAMSCHANGED, 0, 0);
if copy(Buf, 1, Length(cmd_FunctionParamsChanged)) =
cmd_FunctionParamsChanged then
begin
if Length(Buf) = Length(cmd_FunctionParamsChanged) +
Length(cmd_Terminator) then
PostMessage(Application.MainForm.Handle, WM_PARAMSCHANGED,
1, -1)
else
PostMessage(Application.MainForm.Handle, WM_PARAMSCHANGED, 1,
StrToInt(copy(Buf, Length(cmd_FunctionParamsChanged) + 2, Length(Buf) -
Length(cmd_FunctionParamsChanged) - 2)));
end;
if Buf = cmd_ShutDownApp then
PostMessage(Application.MainForm.Handle, WM_CLOSE, 1, 0);
if copy(Buf, 1, Length(cmd_AdminMessage)) = cmd_AdminMessage then
begin
Buf_1 := copy(Buf, Length(cmd_AdminMessage) + 2, Length(Buf) -
Length(cmd_AdminMessage) - 2);
GetMem(Ptr, Length(Buf_1) + 1);
StrPCopy(PChar(Ptr), Buf_1);
PostMessage(Application.MainForm.Handle, WM_ADMINMESSAGE,
Longint(Ptr), 0);
end;
if copy(Buf, 1, Length(cmd_UserMessage)) = cmd_UserMessage then
begin
Buf_1 := copy(Buf, Length(cmd_UserMessage) + 2, Length(Buf) -
Length(cmd_UserMessage) - 2);
GetMem(Ptr, Length(Buf_1) + 1);
StrPCopy(PChar(Ptr), Buf_1);
PostMessage(Application.MainForm.Handle, WM_USERMESSAGE,
Longint(Ptr), 0);
end;
end;
Sleep(1);
end;
Sock.CloseSocket;
finally
freeAndNil(Sock);
end;
end;
Jedna se o vlakno, ktere ma otevreny konkretni UDP port pro prichozi
pozadavky. Na nektere pozadavky posila v ramci daneho spojeni odpoved a na
nektere ne - jen zasle zpravu hlavnimu oknu aplikace zpravu.
Toto okno ji zpracuje nasledovne:
procedure TG3WMainForm.WndProc(var Message: TMessage);
var
P: PChar;
i: integer;
begin
if (Message.Msg = WM_CLOSE) and (Message.WParam = 1) then
Application.Terminate;
if Message.Msg = WM_PARAMSCHANGED then
begin
case Message.WParam of
0: Gor3WinApp.Params.LoadParams([ptApp]);
1: Gor3WinApp.Params.LoadParams([ptFunction]);
end;
for i := 0 to Pred(Screen.FormCount) do
if (Screen.Forms[i] <> self) and (Screen.Forms[i] is TG3WForm) then
SendMessage(Screen.Forms[i].Handle, WM_PARAMSCHANGED,
Message.wParam, Message.lParam);
end;
if Message.Msg = WM_ADMINMESSAGE then
begin
P := StrNew(PChar(Message.wParam));
try
FreeMem(Pointer(Message.wParam), Length(P) + 1);
if IsIconic(Application.Handle) then
FlashWindow(Application.Handle, true);
Application.MessageBox(PChar(HexStrToStr(P)), 'Zprava administratora',
MB_OK + MB_ICONINFORMATION);
finally
StrDispose(P);
end;
end;
if Message.Msg = WM_USERMESSAGE then
begin
P := StrNew(PChar(Message.wParam));
try
FreeMem(Pointer(Message.wParam), Length(P) + 1);
UserMessage(HexStrToStr(P));
finally
StrDispose(P);
end;
end;
inherited;
end;
V pripade kdy dotycna zprava nedojde, tak samozrejme nedojde k uvolneni
alokovane pameti. Vim, ze v pripade ukonceni aplikace dojde i k uvolneni
veskere pameti procesu, kde byla dotycna pamet alokovana, ale preci jen se
mi to vubec nelibi... Jakym zpusobem lze tedy predavat dynamicky alokovane
parametry a korektne je uvolnovat ???
S pozdravem
Milan Tomes
> [mailto:delphi-l-owner@clexpert.cz]On Behalf Of delphin@post.cz
> Sent: Tuesday, October 19, 2004 12:16 PM
>
> > Ted jsem si uvedomil, ze to vlastne take tak delam a jako parametr
> predavam
> > pointer na dynamicky alokovany buffer se stringem. A tak bych se ted rad
> > zeptal ja:
> > 1. Muze se stat, ze zprava neni zpracovana ???
> > 2. Pokud ano, tak za jakych podminek ???
> > 3. Pokud ano, tak kde mam zajistit korektni dealokaci pameti ???
>
> Zpravy jsou zpracovavany dokud existuje prislusny formular a neni ukoncena
> aplikace. Po ukonceni aplikace mohou nektere zpravy zustat nevyrizeny.
Odpovedá: delphin@post.cz
19. 10. 2004 11:53
> V pripade kdy dotycna zprava nedojde, tak samozrejme nedojde k uvolneni
> alokovane pameti. Vim, ze v pripade ukonceni aplikace dojde i k uvolneni
> veskere pameti procesu, kde byla dotycna pamet alokovana, ale preci jen se
> mi to vubec nelibi... Jakym zpusobem lze tedy predavat dynamicky alokovane
> parametry a korektne je uvolnovat ???
V takovem pripade je nutne parametry nekde udrzovat. Resenim je napriklad
predavat je pres TThreadList a hlavnimu vlaknu pote posilat univerzalni
zpravu a pripravenosti konkretni zpravy v TThreadListu. Pri ukonceni
aplikace neni pote problem nevyrizene zpravy korektne uvolnit.